package node.decl;

import grammar.Lexer;
import grammar.MyError;
import lexical.Key;
import lexical.Word;
import midcode.MidCode;
import midcode.MidcodeGenerator;
import midcode.OPType;
import node.Node;
import node.exp.ConstExp;
import symbol.Symbol;
import symbol.SymbolTable;

import java.util.ArrayList;

public class VarDef implements Node {
    private final Lexer lexer;
    private final SymbolTable table;
    private final Node father;
    private int dim = 0;
    private Symbol symbol;
    private InitVal initVal;
    private ArrayList<Node> nodes = new ArrayList<>();
    private ArrayList<ConstExp> exps = new ArrayList<>();

    public VarDef(Lexer lexer, SymbolTable table, Node father) {
        this.lexer = lexer;
        this.table = table;
        this.father = father;
        parseVarDef();
    }

    private void parseVarDef() {
        Word word = lexer.getWord(0);
        nodes.add(lexer.getCurWord()); // ident
        while (lexer.isWordType(0, Key.LBRACK)) {
            dim++;
            nodes.add(lexer.getCurWord()); // [
            ConstExp exp = new ConstExp(lexer, table, this);
            nodes.add(exp);
            exps.add(exp);
            if (lexer.isWordType(0, Key.RBRACK)) {
                nodes.add(lexer.getCurWord()); // ]
            } else {
                lexer.addError(new MyError(lexer.getWord(-1).lineNum, 'k'));
            }
        }
        if (table.redefine(word.word)) {
            lexer.addError(new MyError(word.lineNum, 'b')); // error b
        }
        symbol = new Symbol("int", dim, word, table.getSymbolId());
        table.addSymbol(symbol);
        if (lexer.isWordType(0, Key.ASSIGN)) {
            nodes.add(lexer.getCurWord()); // =
            initVal = new InitVal(lexer, table, this);
            nodes.add(initVal);
        }
    }

    private void setSymbolDim() {
        if (dim == 1) {
            symbol.setLen_1(exps.get(0).getValue());
        } else if (dim == 2) {
            symbol.setLen_1(exps.get(0).getValue());
            symbol.setLen_2(exps.get(1).getValue());
        }
        symbol.setSize();
    }

    private void setSymbolValue() {
        if (dim == 0) {
            symbol.setVal_0(initVal.getValue_0());
        } else if (dim == 1) {
            symbol.setVal_1(initVal.getValue_1());
        } else {
            symbol.setVal_2(initVal.getValue_2());
        }
        symbol.setSize();
    }

    @Override
    public Node getFather() {
        return father;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (Node word : nodes) {
            sb.append(word.toString());
        }
        return sb.append("<VarDef>\n").toString();
    }

    @Override
    public String genMidCode() {
        MidcodeGenerator mg = MidcodeGenerator.getInstance();
        if (mg.getFuncName().equals("global")) {       //全局变量
            if (initVal == null) {
                setSymbolDim();
            } else {
                setSymbolValue();
            }
            symbol.setOffset_gp();
            mg.addGlobalDecl(new MidCode(OPType.GLOBAL_VAL, null, null, symbol.getName()));
        } else {           //局部变量
            setSymbolDim();
            if (initVal != null) {
                int i = 0;
                if (dim == 0) {
                    mg.addMidcode(new MidCode(OPType.ASSIGN, symbol.getName(), null, initVal.genMidCode()));
                } else if (dim == 1) {
                    for (InitVal init : initVal.getInitVals()) {
                        mg.addMidcode(new MidCode(OPType.SW_ARRAY, symbol.getName(), Integer.toString(i++), init.genMidCode()));
                    }
                } else {
                    assert dim == 2;
                    for (InitVal list : initVal.getInitVals()) {
                        for (InitVal init : list.getInitVals()) {
                            mg.addMidcode(new MidCode(OPType.SW_ARRAY, symbol.getName(), Integer.toString(i++), init.genMidCode()));
                        }
                    }
                }
            }
        }
        return "";
    }
}
